#include <QString>
#include <QCoreApplication>
#include <QFileInfo>
#include <QMessageBox>
#include <QProcess>
#include <QTextStream>
#include <QDomDocument>
#include <QCoreApplication>
#include <QXmlStreamReader>
#include <QFileInfo>

#include "moniersedict.h"

#include "transliterator.h"

#include "teiheader.h"

bool slp1LessThan(QString a, QString b)
{
  //  printf("%s|%s\n",a.toStdString().c_str(), b.toStdString().c_str());
    //if b contains a, true;
    if(b.length() >= a.length() && b.startsWith(a))
        return true;
    else if(a.length() > b.length() && a.startsWith(b))
        return false;

    //since SLP1 encode method only had two string the length > 1.
    //we just replace it with Something did not conflict.

    QStringList slp1Order;
    slp1Order << "a" << "A" <<"i"<<"I"<<"u"<<"U"
            <<"f"<<"F"<<"x"<<"X"<<"e"<<"E"<<"o"<<"O"<<"M"<<"H"
            <<"k"<<"K"<<"g"<<"G"<<"N"<<"c"<<"C"<<"j"<<"J"<<"Y"
            <<"w"<<"W"<<"q"<<"Q"<<"R"<<"t"<<"T"<<"d"<<"D"<<"n"
           <<"p"<<"P"<<"b"<<"B"<<"m"<<"y"<<"r"<<"l"<<"v"<<"S"<<"z"<<"s"<<"h"<<"L"<<"|";

    //calc the length, get the min.
    //this what we need compare.
    int length = qMin(a.length(), b.length());

    for(int i = 0; i <length; i++){
        int ai = slp1Order.indexOf(QString(a.at(i)));
        int bi = slp1Order.indexOf(QString(b.at(i)));
        if(ai < bi)
            return true;
        if(ai > bi)
            return false;
    }

    return false;
}

MonierSEDict::MonierSEDict()
{
    QString xmlfile = QCoreApplication::applicationDirPath()+"/dict/monierse/monier.xml";


    //load external idx, since monier.xml maybe updated
    //if monier.xml is not match the shipped monier.idx,
    //inform user to update the idx.
    QString idxfile = QCoreApplication::applicationDirPath()+"/dict/monierse/monier.idx";


    //we put all this files in resource.qrc

    QFileInfo idxinfo(idxfile);
    QFileInfo xmlinfo(xmlfile);
    if(!xmlinfo.exists()) {
        QMessageBox msgBox;
        msgBox.setText(QObject::tr("The \"monier.xml\" file not exists.\n"
                          "Please download it first.\n"));
        msgBox.exec();
        exit(0);
    }

    if(!xmlinfo.isReadable()) {
        QMessageBox msgBox;
        msgBox.setText(QObject::tr("You had no read permission on \"monier.xml\"\n"
                          "Please correct it.\n"));
        msgBox.exec();
        exit(0);
    }

    if(!idxinfo.exists()) {
        //the external idx is not exists.
        //let check the xmlfile.
        //this is the monier.xml file size which we use.
        qint64 xmlfilesize = 74967874;

        //monier.xml updated. warn user to re-generate the idx file.
        if(xmlinfo.size() != xmlfilesize){
            QMessageBox msgBox;
            msgBox.setText(QObject::tr("The monier.xml has been updated.\n"
                           "Press 'OK' to run Index Generator to generate the idx file\n"
                           "And put the 'monier.idx' into the dir same as monier.xml\n\n"
                           "Otherwise, the App can not work as expected!!!"));
            msgBox.exec();
            QString gencmd = QCoreApplication::applicationDirPath() + "/genmonieridx2";
            QProcess::startDetached(gencmd);
            exit(0);
        } else //check passed, we use internal idx.
            idxfile = ":/indexes/monier.idx";
    }

    QString greekfile = ":/indexes/monierse-greek.idx";
    QString wnafile = ":/indexes/monierse-works_and_authors.idx";
    QString scanfile = ":/indexes/monierse-scanjpg.idx";

    idx = createMonierSEIndex(idxfile).first;
    ref = createMonierSEIndex(idxfile).second;

    scanidx = createMonierSEScanIdx(scanfile);
    greekidx = createMonierSEGreekIdx(greekfile);
    wnaidx = createWorksAndAuthorsIdx(wnafile);

    //qSort(allkey.begin(), allkey.end(), slp1LessThan);
    monierXml = new QFile(xmlfile);
    //here need check succ or NOT.
    monierXml->open(QIODevice::ReadOnly | QIODevice::Text);

    transliterator = new Transliterator();

    settings = new QSettings("Cjacker", "Abhidhaana");


    this->keyword = "";
}

MonierSEDict::~MonierSEDict()
{
    monierXml->close();
}


DictionaryDescription MonierSEDict::getDescription()
{

    QString acknowledgefile = QCoreApplication::applicationDirPath()+"/dict/monierse/MWHeader.xml";

    TeiHeader *teih = new TeiHeader(acknowledgefile);

    QString teiinfo;
    teiinfo += QString("<html><body style=\"font-size:10pt;\"><p>This infomation is generated from '%1'' automatically, "
                    "for more informations, please refer to '%1' and related website below.</p>").arg(QFileInfo(acknowledgefile).fileName());

    teiinfo += teih->toHtml();

    teiinfo += "<br><b>Related website:</b><br>"
            "<a href=\"http://www.sanskrit-lexicon.uni-koeln.de\">"
            "Cologne Digital Sanskrit Dictionaries and Online version</a>";
    teiinfo += "<br>"
            "<a href=\"http://www.sanskritlibrary.org\">The Sanskrit Library</a>";

    teiinfo += "</body></html>";

    DictionaryDescription d;
    d.setName("MonierWilliams Sanskrit-English Dictionary");
    d.setShortName("MW Sans-Eng");
    d.setIcon(QIcon(":/mwicon.png"));
    d.setAbout(teiinfo);
    d.setFromLang(DictionaryDescription::Sanskrit);
    d.setToLang(DictionaryDescription::English);
    d.setVersion("1.0");
    return d;
}

void MonierSEDict::slotSaveTransliteration(int i)
{
    settings->setValue("MonierSETrans", i);
}

QWidget * MonierSEDict::getToolbarFilterWidget()
{
    transCombo = new QComboBox(0);
    transCombo->addItem("Devanagari");
    transCombo->addItem("SLP1");
    transCombo->addItem("ITrans");
    transCombo->addItem("Harward-Kyoto");
    transCombo->addItem("Velthuis");
    transCombo->addItem("IAST");

    connect(transCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSaveTransliteration(int)));
    connect(transCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(filterChanged()));
    int cur = settings->value("MonierSETrans", 0).toInt();
    if(cur >= transCombo->count())
        cur = 0;
    transCombo->setCurrentIndex(cur);

    //if(cur == 0)
    //listfontname = "Sanskrit 2003";
    /*else
        listfontname = QString();*/

    return transCombo;
}

QString MonierSEDict::textToSlp1(QString text)
{
    if(text.isEmpty() || text.isNull() || text == "")
        return "";

    int method = transCombo->currentIndex();

    QString source_string = text;//.toUtf8().constData();

    QString itrans;
    switch(method) {
    case 0:
        itrans = transliterator->deva_to_itrans(source_string);
        break;
    case 1:
        itrans = transliterator->slp1_to_itrans(source_string);
        break;
    case 2:
        itrans = source_string;
        break;
    case 3:
        itrans = transliterator->hk_to_itrans(source_string);
        break;
    case 4:
        itrans = transliterator->velthuis_to_itrans(source_string);
        break;
    case 5:
        itrans = transliterator->iast_to_itrans(source_string.toUtf8().constData());
        break;
    }
    return transliterator->itrans_to_slp1(itrans);
}


QString MonierSEDict::slp1ToText(QString text)
{
    QString itrans = transliterator->slp1_to_itrans(text);

    int method = transCombo->currentIndex();

    QString result;
    switch(method) {
    case 0:
        result = transliterator->itrans_to_deva(itrans);
        break;
    case 1:
        result = text;
        break;
    case 2:
        result = itrans;
        break;
    case 3:
        result = transliterator->itrans_to_hk(itrans);
        break;
    case 4:
        result = transliterator->itrans_to_velthuis(itrans);
        break;
    case 5:
        result = transliterator->itrans_to_iast(itrans);
        break;
    }

    return result;
}


QString MonierSEDict::slp1ToDeva(QString slp1)
{
    QString itrans = transliterator->slp1_to_itrans(slp1);

    return transliterator->itrans_to_deva(itrans);
}

QPair<MonierSEIndex,MonierSERef> MonierSEDict::createMonierSEIndex(QString idxfile)
{
    MonierSEIndex index;
    MonierSERef ref;

    QFile file(idxfile);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return QPair<MonierSEIndex, MonierSERef>(index, ref);
    while (!file.atEnd()) {
        //this will cause startup very slow under Mac.
#if defined(Q_OS_LINUX)
        qApp->processEvents();
#endif
        QByteArray line = file.readLine();
        QStringList linelist = QString(line).split(";");
        QString key = linelist[0];

        //also setup allkey String List.
        //if(this->allkey.indexOf(key) == -1)
        this->allkey.append(key);

        qint64 pos = linelist[1].toLongLong();
        int offset = linelist[2].toUInt();
        MonierSEPosition p;
        p.first = pos;
        p.second = offset;
        index.insert(key, p);

        QString lnum = linelist[3];
        ref.insert(lnum, key);
    }
    file.close();
    return QPair<MonierSEIndex, MonierSERef>(index, ref);
}


MonierSEScan MonierSEDict::createMonierSEScanIdx(QString scanfile)
{
    MonierSEScan scanidx;
    QFile file(scanfile);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return scanidx;

    while (!file.atEnd()) {
        QByteArray line = file.readLine();
        QStringList linelist = QString(line).split(";");
        QString page = linelist[0];
        QString link = linelist[1];
        scanidx.insert(page, link);
    }
    file.close();
    return scanidx;
}

MonierSEGreek MonierSEDict::createMonierSEGreekIdx(QString greekfile)
{
    MonierSEGreek greekidx;
    QFile file(greekfile);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return greekidx;

    while (!file.atEnd()) {
        QByteArray line = file.readLine();
        QStringList linelist = QString(line).split(":");
        QString key = linelist[0];
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
        QString greek = linelist[1];
#else
        QString greek = QString::fromUtf8(linelist[1].toLatin1().constData());
#endif
        QString num = linelist[2];
        greekidx.insert((key+num).simplified(), greek);
    }
    file.close();
    return greekidx;
}

WorksAndAuthors MonierSEDict::createWorksAndAuthorsIdx(QString wnafile)
{
    WorksAndAuthors wnaidx;
    QFile file(wnafile);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return wnaidx;
    while (!file.atEnd()) {
        QByteArray line = file.readLine().simplified();
        QStringList linelist = QString(line).split("@");
        if(linelist.size() < 2)
            continue;
        QString key = linelist[0].simplified().toLower();
        QString type = linelist[1].simplified();
        QString entry;

        //that means unknown.
        if(type.contains("?"))
            entry = "?";
        else
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
            entry = linelist[2];
#else
            entry = QString::fromUtf8(linelist[2].toLatin1().constData());
#endif
        wnaidx.insert(key, QPair<QString, QString>(type, entry));
    }
    return  wnaidx;
}


QString MonierSEDict::getScanJpgLink(QString page)
{
    QString link = "";
    link = scanidx.value(page);
    return link;
}

QString MonierSEDict::getGreekWord(QString key)
{
    QString word = "";
    word = greekidx.value(key);
    return word;
}


QString MonierSEDict::getTooltip(QString protocol, QString key)
{
    if(protocol == "tooltipwna") {
        QString tip = wnaidx.value(key).second;
        if(tip.isEmpty() || tip.isNull())
            tip = "Unknown";
        return "<span style=\"font-family: 'Sanskrit 2003'; font-size: 11pt; color: blue;\">" + tip + "</span>";
    }
    return "";
}

MonierSEItemList MonierSEDict::findMonierSEIndex(QString key)
{
    MonierSEItemList list;
    MonierSEIndexIterator i = idx.find(key);
    while (i != idx.end() && i.key() == key) {
        list.push_back(*i);
        ++i;
    }
    qSort(list.begin(), list.end());
    return list;
}

QString MonierSEDict::getRefWord(QString lnum)
{
    QString word = "";
    word = ref.value(lnum);
    return word;
}

static bool shouldRemove(QString first, QString second)
{
    return (first == second);
}

/*

//always return a devanagari list.
QStringList MonierSEDict::getCandidateWords(QString key)
{
    keyword = textToSlp1(key);

    QStringList nextList;

    if(transCombo->currentIndex() == 0 && !transliterator->is_devanagari(key)) {
          QMessageBox msgBox;
          msgBox.setText(tr("What you input is not a valid Devanagari string."));
          msgBox.exec();
          return nextList;
    }

    QString word = keyword;

    //your input maybe not in allkey, since allkey store the keywords of dict
    //and your input may not be a keyword.
    //so this function may return a Empty QStringList.

    int id = allkey.indexOf(keyword);
    //this usally means you click the sidebar of wordlist.
    //since index contains the word, id will never -1.
    //note, here we sort from the second record to avoid make a mistake,
    //the sort will keep the clicked word top.
    if(id != -1) {
        nextList = allkey.mid(id,100);
        qSort(nextList.begin()+1, nextList.end(), slp1LessThan);
        //remove duplicated items in nextList.
        QStringList::iterator it = std::unique (nextList.begin(), nextList.end(), shouldRemove);
        nextList.erase(it, nextList.end());
        return slp1ListToDevaList(nextList);
    }

    //at start, you may input such as "k" or "praj~n", it is not in allkey.
    //I will append a "a" to it and try again.
    //It should have a "xa" word always.
    if(id == -1) {
        id = allkey.indexOf(keyword+"a");
    }

    if(id != -1) {
        nextList = allkey.mid(id,100);
        qSort(nextList.begin()+1, nextList.end(), slp1LessThan);
        //remove duplicated items in nextList.
        QStringList::iterator it = std::unique (nextList.begin(), nextList.end(), shouldRemove);
        nextList.erase(it, nextList.end());
        return slp1ListToDevaList(nextList);
    }

    //we will never guess what you input, and just return a Empty StringList.
    return slp1ListToDevaList(nextList);

}
*/

QList<QPair<QString, QString> > MonierSEDict::getCandidateWords(QString key)
{


    QList<QPair<QString, QString> > wordPairList;

    QStringList nextList;

    if(transCombo->currentIndex() == 0 && !transliterator->is_devanagari(key)) {
          QMessageBox msgBox;
          msgBox.setText(tr("What you input is not a valid Devanagari string."));
          msgBox.exec();
          return wordPairList;
    }
    keyword = textToSlp1(key);
    //your input maybe not in allkey, since allkey store the keywords of dict
    //and your input may not be a keyword.
    //so this function may return a Empty QStringList.

    int id = allkey.indexOf(keyword);
    //this usally means you click the sidebar of wordlist.
    //since index contains the word, id will never -1.
    //note, here we sort from the second record to avoid make a mistake,
    //the sort will keep the clicked word top.
    if(id != -1) {
        nextList = allkey.mid(id,100);
        qSort(nextList.begin()+1, nextList.end(), slp1LessThan);
        //remove duplicated items in nextList.
        QStringList::iterator it = std::unique (nextList.begin(), nextList.end(), shouldRemove);
        nextList.erase(it, nextList.end());

        for(int i = 0; i < nextList.size(); i++) {
                wordPairList.append(QPair<QString, QString>(slp1ToText(nextList.at(i)), slp1ToDeva(nextList.at(i))));
        }
        return wordPairList;
    }

    //at start, you may input such as "k" or "praj~n", it is not in allkey.
    //I will append a "a" to it and try again.
    //It should have a "xa" word always.
    if(id == -1) {
        id = allkey.indexOf(keyword+"a");
    }

    if(id != -1) {
        nextList = allkey.mid(id,100);
        qSort(nextList.begin()+1, nextList.end(), slp1LessThan);
        //remove duplicated items in nextList.
        QStringList::iterator it = std::unique (nextList.begin(), nextList.end(), shouldRemove);
        nextList.erase(it, nextList.end());
        for(int i = 0; i < nextList.size(); i++)
                wordPairList.append(QPair<QString, QString>(slp1ToText(nextList.at(i)), slp1ToDeva(nextList.at(i))));

        return wordPairList;
    }

    //we will never guess what you input, and just return a Empty StringList.
    return wordPairList;
}


QStringList MonierSEDict::slp1ListToDevaList(QStringList slp1List)
{
    QStringList devaList;
    for (int i = 0; i < slp1List.size(); i++) {
        //devaList << slp1ToDeva(slp1List.at(i));
        devaList << slp1ToText(slp1List.at(i));
    }
    return devaList;
}

QString MonierSEDict::readMonierSEContent(qint64 pos, int offset)
{
    monierXml->seek(pos);
    return monierXml->read(offset);
}

void MonierSEDict::queryWord(QString key)
{
    //this is hack, since we output candidate word list to devanagari always.
    //if you clicked the sidebar of dictionary, if will query a devanagari string directly.
    //here we added a detection. and make the plugin loose connection with GUI.

    if(transCombo->currentIndex() == 0 && !transliterator->is_devanagari(key)) {
          QMessageBox msgBox;
          msgBox.setText(tr("What you input is not a valid Devanagari string."));
          msgBox.exec();
          emit readyToShow(QString());
          return;
    }

    if(transliterator->is_devanagari(key)) {
        QString itrans = transliterator->deva_to_itrans(key);
        keyword = transliterator->itrans_to_slp1(itrans);
    }
    else
        keyword = textToSlp1(key);


    QStringList wordrecords = findWords(keyword);

    QString htmlContent = "<html><body style=\"font-size:10pt;\"><meta name=\"qrichtext\" content=\"1\"/>";

    for(int i = 0; i < wordrecords.size(); i++){
        htmlContent += "<p>";
        htmlContent += processLine(wordrecords.at(i).simplified());
        htmlContent +="</p>";
    }

    htmlContent += "</body></html>";
    emit readyToShow(htmlContent);
}


QStringList MonierSEDict::findWords(QString key)
{
    QStringList ret;
    MonierSEItemList list = findMonierSEIndex(key);
    for (int i = 0; i < list.size(); ++i) {
        MonierSEPosition p = list.at(i);
        qint64 pos = p.first;
        int offset = p.second;
        //printf("%lld;%d;\n", pos,offset);
        QString content = readMonierSEContent(pos, offset);
        ret<<content;
        //printf("%s\n", content.toStdString().c_str());
    }
    return ret;
}





//try search dakShiNAchAritantra
QString MonierSEDict::markup_devanagari(QString string)
{
    QString result_string = "";
    QXmlStreamReader xml(string);
    while (!xml.atEnd() && !xml.hasError())
    {
        xml.readNext();
        QString name = xml.name().toString();
        QString text = xml.text().toString();
        if(xml.isStartElement() && name == "shortlong")
            continue;

        if(xml.isStartElement() && name == "TWOWORDS")
            continue;

        if(xml.isStartElement() && name == "hom")
            continue;

        if(xml.isStartElement() && name == "root") {
            result_string += QString::fromUtf8("√");
        }
        if(xml.isStartElement() && (name == "srs" || name =="srs1" )) {
            result_string += QString::fromUtf8("*");
        }
        if(xml.isStartElement() && (name == "sr" || name =="sr1" )) {
            result_string += QString::fromUtf8("°");
        }
        result_string += text;
    }

    //drop accent mark, it will break transliteration.
    result_string.replace("/","");

    //trans here.
    QString itrans_string = transliterator->slp1_to_itrans(result_string);
    result_string = transliterator->itrans_to_deva(itrans_string);
    result_string += " <span style=\"font-size: 10pt\">/" + transliterator->itrans_to_iast(itrans_string.toUtf8()) + "/</span>";

    //post fix
    result_string.replace(QString::fromUtf8("√"), QString::fromUtf8("<span style=\"color:#5500ff;\">√</span>"));
    result_string.replace(QString::fromUtf8("*"), QString::fromUtf8("<span style=\"color:#ff0000;\">*</span>"));
    result_string.replace(QString::fromUtf8("°"), QString::fromUtf8("<span style=\"color:#ff0000;\">°</span>"));
    return result_string;
}


QString MonierSEDict::markup_pagecolumn(QString pc_string)
{
    QStringList pagecolumn = pc_string.split(",");
    QString page = pagecolumn.at(0);
    //mistake in monier.xml.
    page = page.replace("Page","");
    QString column = pagecolumn.at(1);
    QString pageidx = QString("%1").arg(page, 4, QChar('0'));
    QString link = this->getScanJpgLink(pageidx).simplified();

    QFileInfo localimg(QCoreApplication::applicationDirPath()+"/dict/monierse/scan/mw0001-a.jpg");

    QString baseurl = "file://" + QCoreApplication::applicationDirPath() + "/dict/monierse/scan/";

    if(!localimg.exists())
        baseurl = "http://www.sanskrit-lexicon.uni-koeln.de/scans/MWScan/MWScanjpg/";

    link = baseurl + link;

    //the default link color "blue" is too attractive...
    //we used anther color to render the link.
    QString result = "<a href=\""
            + link
            + QString("\"><span style=\"font-size:8pt;color:#8d8d6a\">Page=%1, Column=%2</span></a>").arg(page).arg(column);
    return result;
}

//try 'vas'
//greek words.
//http://www.sanskrit-lexicon.uni-koeln.de/mwupdate/greek/web/disp/
QString MonierSEDict::markup_body(QString string)
{
    QString result_string = "";
    QString sanskrit_string = "";

    bool in_sanskrit_context = false;

    //as0/asp0 had been substitute with <s>
    bool in_as0_drop_context = false;

    QXmlStreamReader xml(string);
    while (!xml.atEnd() && !xml.hasError())
    {
        xml.readNext();

        QString name = xml.name().toString();
        QString text = xml.text().toString();

        //unprocessed element.
        if(xml.isStartElement() && name == "abE")
            continue;

        if(xml.isStartElement() && (name == "shortlong" || name == "shc"))
            continue;

        if(xml.isStartElement() && name == "hom")
            continue;

        if( name == "ls") {
            if(xml.isStartElement()) {
                QString ab_of_works_and_authors = "";
                ab_of_works_and_authors += text;
                xml.readNext();
                QString intername = xml.name().toString();
                QString intertext = xml.text().toString();
                ab_of_works_and_authors += intertext;
                while(intername != "ls" && xml.isEndElement()) {
                    xml.readNext();
                    intername = xml.name().toString();
                    intertext = xml.text().toString();
                    ab_of_works_and_authors += intertext;
                }
                //fix ab_of_works_and_authors
                ab_of_works_and_authors = ab_of_works_and_authors.replace("_"," ");
                //some ab contains pages and something else, after replace "_" to " ".
                //It should be safe to get the first one.
                QString ab = ab_of_works_and_authors.split(" ").at(0);
                if(!ab.endsWith("."))
                    ab += ".";
                result_string += QString("<a href=\"tooltipwna://%1\">").arg(ab.toLower().simplified()) + ab_of_works_and_authors + "</a>";
                //printf("%s\n",ab_of_works_and_authors.toLatin1().constData());
                continue;
            }
        }
        if( name == "as0" ||name == "asp0" ) {
            if(xml.isStartElement()) {
                in_as0_drop_context = true;
                continue;
            }
            else
                in_as0_drop_context = false;
        }

        if(in_as0_drop_context)
            continue;

        //sanskrit context need proccessed specially.
        if(name == "s") {
            if(xml.isStartElement()) {
                in_sanskrit_context = true;
            }
            else {
                in_sanskrit_context = false;

                //process the OM, it is very special.
                if(sanskrit_string == "om")
                    sanskrit_string = "OM";

                sanskrit_string.replace("/","");

                //trans here.
                QString itrans_string = transliterator->slp1_to_itrans(sanskrit_string);
                sanskrit_string = transliterator->itrans_to_deva(itrans_string);
                sanskrit_string += "<span style=\"font-size: 9pt; color:#55557f;\"> /" + transliterator->itrans_to_iast(itrans_string.toUtf8()) + "/</span>";

                //post fix
                sanskrit_string.replace(QString::fromUtf8("√"), QString::fromUtf8("<span style=\"color:#5500ff;\">√</span>"));
                sanskrit_string.replace(QString::fromUtf8("*"), QString::fromUtf8("<span style=\"color:#ff0000;\">*</span>"));
                sanskrit_string.replace(QString::fromUtf8("°"), QString::fromUtf8("<span style=\"color:#ff0000;\">°</span>"));

                //adjust the size and color of sanskrit devanagari output to make it easy read.
                //result_string += "<span style=\"font-family:'Sanskrit 2003';color:#00aa7f;\">" + sanskrit_string + "</span>";
                result_string += "<span style=\"font-family:'Sanskrit 2003';font-size: 12pt;color:#00aa00;\">"
                        + sanskrit_string + "</span>";
                sanskrit_string = "";
            }
        }

        //sanskrit context
        //save text to sanskrit-string.
        if(in_sanskrit_context) {
            if(name == "b" || name == "b1"){
                if(xml.isStartElement())
                    sanskrit_string += "[";
                else
                    sanskrit_string += "]";
            }
            if(name == "p" || name == "p1"){
                if(xml.isStartElement())
                    sanskrit_string += "(";
                else
                    sanskrit_string += ")";
            }
            if(xml.isStartElement() && name == "root") {
                sanskrit_string += QString::fromUtf8("√");
            }
            if(xml.isStartElement() && (name == "srs" || name =="srs1" )) {
                sanskrit_string += QString::fromUtf8("*");
            }

            if(xml.isStartElement() && (name == "sr" || name =="sr1" || name == "fcom")) {
                sanskrit_string += QString::fromUtf8("°");
            }
            sanskrit_string += text;
        }

        //non-sanskrit context.
        //save it directly to result_string.
        if(!in_sanskrit_context) {

            //process some greek word in monier.xml
            //gk always in etym, so keep the style same as etym.
            if(xml.isStartElement() && name == "gk") {
                xml.readNext();
                QString gknum = keyword+xml.text().toString();
                QString gkword = this->getGreekWord(gknum);
                result_string += "<span style=\"font-style:italic;  color:#ff5500;\">" + gkword + "</span>";
                continue;
            }

            if(name == "b" || name == "b1"){
                if(xml.isStartElement())
                    result_string += "[";
                else
                    result_string += "]";
            }
            if(name == "p" || name == "p1"){
                if(xml.isStartElement())
                    result_string += "(";
                else
                    result_string += ")";
            }
            if(name == "quote"){
                if(xml.isStartElement())
                    result_string += "\"";
                else
                    result_string += "\"";
            }
            if(xml.isStartElement() && name == "root") {
                result_string += QString::fromUtf8("√");
            }
            if(xml.isStartElement() && name == "amp") {
                result_string += QString::fromUtf8("&");
            }
            if(xml.isStartElement() && name == "eq") {
                result_string += QString::fromUtf8("=");
            }
            if(xml.isStartElement() && name == "auml") {
                result_string += QString::fromUtf8("ä");
            }
            if(xml.isStartElement() && name == "euml") {
                result_string += QString::fromUtf8("ё");
            }
            if(xml.isStartElement() && name == "ouml") {
                result_string += QString::fromUtf8("ö");
            }
            if(xml.isStartElement() && name == "uuml") {
                result_string += QString::fromUtf8("ü");
            }

            if(xml.isStartElement() && name == "fs") {
                result_string += QString::fromUtf8("/");
            }
            if(xml.isStartElement() && name == "msc") {
                result_string += QString::fromUtf8(";");
            }
            if(xml.isStartElement() && (name == "etc" || name == "etc1" || name == "etcetc")) {
                result_string += QString::fromUtf8(" etc. ");
            }

            if(xml.isStartElement() && (name == "srs" || name =="srs1" )) {
                result_string += QString::fromUtf8("*");
            }

            if(xml.isStartElement() && (name == "sr" || name =="sr1" || name == "fcom")) {
                result_string += QString::fromUtf8("°");
            }
            if(xml.isStartElement() && name == "see") {
                //result_string += QString::fromUtf8("»");
                result_string += "<span style=\"font-style:italic;\">" + QString::fromUtf8("»") + "refer to</span>";
            }

            if(xml.isStartElement() && name == "cf") {
                result_string += QString::fromUtf8("<span style=\"font-style:italic;\">c.f.</span>");
            }
            if(xml.isStartElement() && name == "qv") {
                result_string += QString::fromUtf8("<span style=\"font-style:italic;\">q.v.</span>");
            }
            //should keep same style as sanskrit.
            if(name == "ns") {
                if(xml.isStartElement())
                    result_string += "<span style=\"color:#00aa7f;\">";
                else
                    result_string += "</span>";
            }


            if(name == "bio" || name == "bot"){
                if(xml.isStartElement())
                    result_string += "<span style=\"font-size:8pt; color:#B40431;\">";
                else
                    result_string += "</span>";
            }

            if(name == "ab") {
                if(xml.isStartElement())
                    result_string += "<span style=\" color:#55aa00;\">";
                else
                    result_string += "</span>";
            }

            //pc means the meaning of this word sperated in two pages.
            //we ignore here.
            if(name == "pc") {
                if(xml.isStartElement()) {
                    xml.readNext();
                    continue;
                }
            }

            //means reference.
            //we will create a link to that page.
            //form like p._56_,_col._3
            if(name == "pcol") {
                if(xml.isStartElement()) {
                    xml.readNext();
                    QString pc = xml.text().toString();
                    pc = pc.replace("_","").replace("p.","").replace("col.","");
                    QStringList plist = pc.split(",");
                    QString page = QString("%1").arg(plist[0], 4, QChar('0'));
                    QString column = plist[1];
                    QString link = this->getScanJpgLink(page).simplified();

                    QFileInfo localimg(QCoreApplication::applicationDirPath()+"/dict/monierse/scan/mw0001-a.jpg");

                    QString baseurl = "file://" + QCoreApplication::applicationDirPath() + "/dict/monierse/scan/";

                    if(!localimg.exists())
                        baseurl = "http://www.sanskrit-lexicon.uni-koeln.de/scans/MWScan/MWScanjpg/";

                    link = baseurl + link;

                    QString result = "<a href=\""
                            + link
                            + QString("\">p.%1, col.%2</a>").arg(plist[0]).arg(column);
                    result_string += result;
                    continue;
                }
            }

            if(name == "pL" || name == "dL") {
                if(xml.isStartElement()) {
                    xml.readNext();
                    QString lnum = xml.text().toString();
                    QString word = this->getRefWord(lnum);
                    QString itrans_string = transliterator->slp1_to_itrans(word);
                    QString sanskrit_string = transliterator->itrans_to_deva(itrans_string);
                    sanskrit_string += "<span style=\"font-size: 9pt; color:#55557f;\"> /" + transliterator->itrans_to_iast(itrans_string.toUtf8()) + "/</span>";

                    result_string += "<span style=\"font-style:italic;\">" + QString::fromUtf8("»") + "refer to </span><span style=\"font-family:'Sanskrit 2003';color:#00aa7f;\">" + sanskrit_string + "</span>";
                    continue;
                }
            }

            if(name == "etym") {
                if(xml.isStartElement())
                    result_string += "<span style=\"font-style:italic; color:#ff5500;\">";
                else
                    result_string += "</span>";
            }

            if(name == "lex" || name == "vlex") {
                if(xml.isStartElement())
                    result_string += "<span style=\"font-style:italic; color:#FF00BF\">";
                else
                    result_string += "</span>";
            }

            result_string += text;
        }
    }


    //final fix.
    //drop accent mark, it will break transliteration.
    result_string.replace("_"," ");
    result_string.replace("~"," ");


    //post fix
    result_string.replace(QString::fromUtf8("√"), QString::fromUtf8("<span style=\"color:#5500ff;\">√</span>"));
    result_string.replace(QString::fromUtf8("*"), QString::fromUtf8("<span style=\"color:#ff0000;\">*</span>"));
    result_string.replace(QString::fromUtf8("°"), QString::fromUtf8("<span style=\"color:#ff0000;\">°</span>"));
    return result_string;
}

//process a record and return the marked up result_string.
QString MonierSEDict::processLine(QString line)
{

    QString result_string = "";

    //header, we only need key2 and hom.
    QString key2_string = "";
    QString hom_string = "";
    //body,
    QString body_string = "";
    //end, we only need to show page/column.
    //lnum had been processed to a idx with monier.xml
    //And lnum only used as a reference in body.
    QString pc_string = "";
    QString lnum_string = "";


    QDomDocument doc("mydocument");
    doc.setContent(line);
    QDomElement docElem = doc.documentElement();

    QDomNode n = docElem.firstChild();
    while(!n.isNull()) {
        QDomElement e = n.toElement(); // try to convert the node to an element.
        if(!e.isNull()) {
            //header
            if(e.tagName() == "h") {
                QDomNode key2 = n.namedItem("key2");
                QDomNode hom = n.namedItem("hom");
                if(!key2.isNull()) {
                    QTextStream stream(&key2_string);
                    key2.save(stream, 0);

                }
                if(!hom.isNull()){
                    hom_string = hom.toElement().text();
                }
                //if it is a header word(NOT a <HxA> or <HxB>), we show key2 and hom.
                if(!(docElem.tagName().endsWith("A") || docElem.tagName().endsWith("A"))) {
                    result_string += "<span style=\"font-size: 12pt; font-family:'Sanskrit 2003'; color:#0000ff;\">"
                            + markup_key2(key2_string) + "</span>"
                            + "<span style=\"color:#ff0000;\"> "
                            + hom_string +"</span><br>";
                }
            }
            //body
            if(e.tagName() == "body") {
                QTextStream stream(&body_string);
                n.save(stream,0);
                body_string=body_string.simplified();
                result_string += markup_body(body_string);
            }
            //tail
            if(e.tagName() == "tail") {
               QDomNode pc = n.namedItem("pc");
               if(!pc.isNull()){
                    pc_string = pc.toElement().text();
                    result_string += "<br>" + markup_pagecolumn(pc_string);
               }
            }
        }
        n = n.nextSibling();
    }


    return result_string;
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#else
Q_EXPORT_PLUGIN2(moniersedict, MonierSEDict);
#endif
